# -*- coding: utf-8 -*-
"""
Created on Fri Jul 14 14:58:17 2017

@author: decardin

This file constains all the units in the plant. This includes Absorber,
Desorber, Lean-Rich Heat Exchanger and Reboiler. Key assuptions are described 
here,
ABSORBER: 
"""
import numpy as np
from casadi import *
from Models import Stream as Stream
from Properties import Properties
from Models import Column as Column
# import Column as Desorber
from Models import HeatExchanger as HeatExchanger
from Models import Reboiler as Reboiler
from copy import deepcopy
# from operating_points import opt_pts


class PCCPlant:
    """
    The following constants are declared:
        nComponents = 4 : Number of components in the system
        R = 8.314 : Universal gas constant
        DHRXN = 82000 : Heat of reaction
        aP 
        
    """
    # Constants or general parameters
    nComponents = 4
    R = 8.314
    g = 8.80665
    DHRXN = 82000
    aP = 143.9
    dP = 0.038
    
    xss = np.load('xss.npy')
    zss = np.load('xss.npy')
    uss = np.load('uss.npy') # 0.581191*0.94
    # yss = np.load('yss.npy')

    def __init__(self, min_x=None, delta_x=None):
        # Streams
        self.fluegas = Stream.Stream(0.0898912, 319.7, 103825, np.array([0.03571, 0.00781, 0, 0.00112]))
        self.leanAmine = Stream.Stream(6.292384000000001E-4, 314, 101325, np.array([0.0, 1.36, 5, 39.09]))

        # Absorber specs
        self.absHeight = 6.1
        self.absNStages = 5
        self.absDiameter = 0.43
        self.absNStates = self.absNStages * 2 * (self.nComponents + 1)
        self.absorber = Column.Column(self.absHeight, self.absDiameter, 'absorber')
        self.absorber.gasin = deepcopy(self.fluegas)

        # Desorber specs
        self.desHeight = 6.1
        self.desNStages = 5
        self.desDiameter = 0.43
        self.desNStates = self.desNStages * 2 * (self.nComponents + 1)
        self.desorber = Column.Column(self.desHeight, self.desDiameter, 'desorber')

        # Heat exchanger specs
        self.hrxNStates = 2
        self.hrx = HeatExchanger.HeatExchanger()

        # Reboiler specs
        self.rebNStates = 1
        self.reboiler = Reboiler.Reboiler()
        
        ## use the following lines means scaling

        if min_x is not None:
            self.min_x = min_x
        else:
            self.min_x = np.loadtxt('min.txt')
        if delta_x is not None:
            self.delta_x = delta_x
        else:
            self.delta_x = np.loadtxt('delta.txt')
#        for i in range(np.size(self.delta_x)):
#            if self.delta_x[i] <= 1e-10:
#                self.delta_x[i] = 1
#                self.min_x[i] = 0

    # ODEs
    def getODE(self, x_scale, z, u):
        # scaling
        x = x_scale * self.delta_x + self.min_x
        # Get and Split the states into various equipment
        abs_x = x[0:self.absNStates]
        des_x = x[self.absNStates:self.absNStates + self.desNStates]
        hrx_x = x[self.absNStates + self.desNStates:self.absNStates + self.desNStates + self.hrxNStates]
        reb_x = x[
                self.absNStates + self.desNStates + self.hrxNStates:self.absNStates + self.desNStates + self.hrxNStates + self.rebNStates]

        # Get and split inputs
        abs_u = u[0] / 1000  # Lean amine flow volumetric flow
        reb_u = u[1] * 1000  # Reboiler heat input
        abs_d = u[2]

        # Get and split disturbances
        # abs_d = p[0]  # Flue gas volumetric flow
#        abs_d = 1.0
        self.absorber.gasin.Q = 0.0898912 * abs_d

        # Absorption column
        self.absorber.liqin.Q = abs_u
        self.absorber.liqin.P = 101325.0  # Pressure is reduced
        self.absorber.liqin.T = 314.0  # Lean amine is cooled
        self.absorber.liqin.comp[0] = 1e-100  # Nitrogen concentration
        self.absorber.liqin.comp[1] = z[5]
        self.absorber.liqin.comp[2] = 5.0
        self.absorber.liqin.comp[3] = 39.09

        abs_xdot = self.absorber.getValue(abs_x, abs_u, abs_d)

        # Heat exchanger
        self.hrx.shellin = deepcopy(self.absorber.liqout)
        self.hrx.tubein = deepcopy(self.absorber.liqin)
        self.hrx.tubein.T = reb_x[0]
        hrx_xdot = self.hrx.getValue(hrx_x, 0, 0)

        # Reboiler
        self.desorber.liqin = deepcopy(self.hrx.shellout)
        self.desorber.liqin.P = 149750.0  # Pump to increase head
        self.desorber.gasin = deepcopy(self.fluegas)
        self.desorber.getValue(des_x, 0, 0)
        self.reboiler.liqin = deepcopy(self.desorber.liqout)
        reb_xdot = self.reboiler.getValueO(reb_x, z[0:5], reb_u, 0)

        # Desorber
        self.desorber.gasin = deepcopy(self.reboiler.gasout)
        self.desorber.gasin.Q = z[6]
        des_xdot = self.desorber.getValue(des_x, 0.0, 0.0)

        # self.desorber.gasin.P = 170000
        # self.desorber.gasin.Q = z[13]
        # self.desorber.gasin.comp = z[15:19]
        # self.desorber.gasin.T = reb_x[0]
        
        xdot = vertcat(abs_xdot, des_xdot, hrx_xdot, reb_xdot)
        xdot_scale = 1./self.delta_x*xdot
        return xdot_scale

    def getALG(self, x_scale, z):
        # scaling
        x = x_scale * self.delta_x + self.min_x
        # abs_x = x[0:self.absNStates]
        # des_x = x[self.absNStates:self.absNStates+self.desNStates]
        # hrx_x = x[self.absNStates+self.desNStates:self.absNStates+self.desNStates+self.hrxNStates]
        reb_x = x[
                self.absNStates + self.desNStates + self.hrxNStates:self.absNStates + self.desNStates + self.hrxNStates + self.rebNStates]

        res0 = self.reboiler.getValueA(reb_x, z[0:5], 0.0, 0.0)
        # res1 = z[13] - self.reboiler.gasout.Q
        res1 = z[5] - self.reboiler.liqout.comp[1]
        res2 = z[6] - self.reboiler.gasout.Q
        # res3 = z[15:19] - self.reboiler.gasout.comp
        return vertcat(res0, res1, res2)
#        res = vertcat(res0, res1, res2)
#        res_scale = 1./self.delta_x*res
#        return res_scale

### 归一化    
#    def normalization(data):
#    M_m = np.max(data)-np.min(data)
#    return (data-np.min(data)) / M_m
### 标准化        
#    def standardization(data):
#        mu = np.mean(data, axis=0)
#        sigma = np.std(data, axis=0)
#        return (data - mu) / sigma